home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d12 / nansisrc.arc / NANSI.ASM < prev    next >
Encoding:
Assembly Source File  |  1986-05-22  |  28.4 KB  |  1,100 lines

  1.     page    66, 132
  2. ;--- nansi.asm ----------------------------------------------------------
  3. ; New ANSI terminal driver.
  4. ; Optimized for speed in the case of multi-character write requests.
  5. ; (C) 1986 Daniel Kegel, Pasadena, CA
  6. ; May be distributed for educational and personal use only
  7. ; The following files make up the driver:
  8. ;    nansi.asm   - all DOS function handlers except init
  9. ;    nansi_p.asm - parameter parser for ANSI escape sequences
  10. ;    nansi_f.asm - ANSI command handlers
  11. ;    nansi_i.asm - init DOS function handler
  12. ;
  13. ; Daniel Kegel, Bellevue, Washington & Pasadena, California
  14. ; Revision history:
  15. ; 5  july 85: brought up non-ANSI portion except forgot backspace
  16. ; 6  july 85: split off ANSI stuff into other files, added backspace
  17. ; 11 july 85: fixed horrible bug in getchar; changed dosfns to subroutines
  18. ; 12 july 85: fixed some scrolling bugs, began adding compaq flag
  19. ; 9  aug 85:  added cursor position reporting
  20. ; 10 aug 85:  added output character translation
  21. ; 11 aug 85:  added keyboard redefinition, some EGA 80x43 support
  22. ; 10 sept 85: Tandy 2000 support via compaq flag (finding refresh buffer)
  23. ; 30 Jan 86:  removed Tandy 2000 stuff, added graphics mode support
  24. ; 12 feb 86:  added int 29h handler, added PUSHA/POPA, added direct beep,
  25. ;          direct cursor positioning, takeover of BIOS write_tty,
  26. ;          noticed & squashed 2 related bugs in tab expansion
  27. ; 13 feb 86:  Squashed them again, harder
  28. ; 24 feb 86:  There is a bug in the timing code used by the BEEP routine.
  29. ;          If the addition of the beep period to the
  30. ;          BIOS low timer word results in an overflow, the beep will be
  31. ;          supressed. Also made code compatible eith earlier versions
  32. ;          of assembler.
  33. ;------------------------------------------------------------------------
  34.  
  35.     include nansi_d.asm    ; definitions
  36.  
  37.     ; from nansi_f.asm
  38.     extrn    f_escape:near, f_in_escape:near
  39.  
  40.     ; from nansi_p.asm
  41.     extrn    param_end:word, redef_end:word
  42.  
  43.     ; from nansi_i.asm
  44.     extrn    dosfn0:near
  45.  
  46.     ; to nansi_p.asm
  47.     public    f_loopdone
  48.     public    f_not_ansi
  49.     public    f_ansi_exit
  50.  
  51.     ; to both nansi_p.asm and nansi_f.asm
  52.     public    cur_x, cur_y, max_x, cur_attrib
  53.  
  54.     ; to nansi_f.asm
  55.     public    xy_to_regs, get_blank_attrib
  56.     public    port_6845
  57.     public    wrap_flag
  58.     public    cur_parm_ptr
  59.     public    cur_coords, saved_coords, max_y
  60.     public    escvector, string_term
  61.     public    cpr_esc, cprseq
  62.     public    video_mode
  63.     public    lookup
  64.     public    in_g_mode
  65.  
  66.     ; to nansi_i.asm
  67.     public    req_ptr, break_handler
  68.     public    int_29
  69.     if    takeBIOS
  70.     public    new_vid_bios, old_vid_bios
  71.     endif
  72.  
  73.     ; to all modules
  74.     public    xlate_tab_ptr
  75.  
  76. ;--- seg_cs is the CS: override prefix
  77. ; (assembler forgets cs: on second "xlat dummy_cs_byte")
  78. seg_cs    macro
  79.     db    2eh
  80.     endm
  81.  
  82. ;--- push_all, pop_all ------------------------------------------------
  83. ; Save/restore all user registers.
  84. push_all    macro
  85.     if    is_8088
  86.     push    ax
  87.     push    bx
  88.     push    cx
  89.     push    dx
  90.     push    bp
  91.     push    si
  92.     push    di
  93.     else
  94.     pusha
  95.     endif
  96.     endm
  97.  
  98. pop_all macro
  99.     if    is_8088
  100.     pop    di
  101.     pop    si
  102.     pop    bp
  103.     pop    dx
  104.     pop    cx
  105.     pop    bx
  106.     pop    ax
  107.     else
  108.     popa
  109.     endif
  110.     endm
  111.  
  112. keybuf    struc                ; Used in getchar
  113. len    dw    ?
  114. adr    dw    ?
  115. keybuf    ends
  116.  
  117.  
  118. ABS40    segment at 40h
  119.     org    1ah
  120. buffer_head    dw    ?    ; Used in 'flush input buffer' dos call.
  121. buffer_tail    dw    ?
  122.  
  123.     org    49h
  124. crt_mode    db    ?
  125. crt_cols    dw    ?
  126. crt_len        dw    ?
  127. crt_start    dw    ?
  128. cursor_posn    dw    8 dup (?)
  129. cursor_mode    dw    ?
  130. active_page    db    ?
  131. addr_6845    dw    ?
  132. crt_mode_set    db    ?    ; = 7 only if monochrome display adaptor
  133. crt_palette    db    ?
  134.     org    6ch
  135. timer_low    dw    ?    ; low word of time-of-day counter (18.2 hz)
  136.  
  137. ABS40    ends
  138.  
  139.     page
  140.  
  141. CODE    segment byte public 'CODE'
  142. assume    cs:code, ds:code
  143.  
  144.     ; Device Driver Header
  145.  
  146.     org    0
  147.  
  148.     dd    -1            ; next device
  149.     dw    8013h            ; attributes
  150.     dw    strategy        ; request header pointer entry
  151.     dw    interrupt        ; request entry point
  152.     db    'CON'            ; device name (8 char)
  153.     db    5 dup (20h)        ;  ... and 5 blanks)
  154.  
  155.     ; Identification- in case somebody TYPEs the assembled driver
  156.     db    27,'[2J'
  157.     db    "Nansi.sys v2.2"
  158.     ife    is_8088
  159.     db    "(80286)"
  160.     endif
  161.     db    ': New ANSI driver (C) Daniel Kegel, Pasadena, CA 1986'
  162.     db    13, 10, 26
  163.  
  164.  
  165. ;----- variable area --------------------
  166. req_ptr label    dword
  167. req_off dw    ?
  168. req_seg dw    ?
  169.  
  170. wrap_flag    db    1    ; 0 = no wrap past line end
  171. escvector    dw    0    ; state vector of ESCape sequencor
  172. video_mode    db    3    ; ROM BIOS video mode (2=BW, 3=color)
  173. max_y        db    24
  174. max_cur_x    label    word    ; used to get both max & cur at once
  175. max_x        db    79    ; line width (79 for 80x25 modes)
  176. cur_coords    label    word
  177. cur_x        db    0    ; cursor position (0 = left edge)
  178. cur_y        db    0    ;          (0 = top edge)
  179. saved_coords    dw    ?    ; holds XY after a SCP escape sequence
  180. string_term    db    0    ; either escape or double quote
  181. cur_attrib    db    7    ; current char attributes
  182. cur_page    db    0    ; current display page
  183. video_seg    dw    ?    ; segment of video card
  184. f_cptr_seg    dw    ?    ; part of fastout write buffer pointer
  185. cur_parm_ptr    dw    ?    ; last byte of parm area now used
  186. port_6845    dw    ?    ; port address of 6845 card
  187. xlate_tab_ptr    dw    ?    ; pointer to output translation table
  188.         if    takeBIOS
  189. old_vid_bios    dd    ?    ; pointer to old video bios routine
  190.         endif
  191.  
  192. brkkeybuf    db    3    ; control C
  193. fnkeybuf    db    ?    ; holds second byte of fn key codes
  194. cpr_buf        db    8 dup (?), '['
  195. cpr_esc        db    1bh    ; descending buffer for cpr function
  196.  
  197. ; following four keybufs hold information about input
  198. ; Storage order determines priority- since the characters making up a function
  199. ; key code must never be separated (say, by a Control-Break), they have the
  200. ; highest priority, and so on.    Keyboard keys (except ctrl-break) have the
  201. ; lowest priority.
  202.  
  203. fnkey    keybuf    <0, fnkeybuf>    ; fn key string (0 followed by scan code)
  204. cprseq    keybuf    <0>        ; CPR string (ESC [ y;x R)
  205. brkkey    keybuf    <0, brkkeybuf>    ; ^C
  206. xlatseq keybuf    <0>        ; keyboard reassignment string
  207.  
  208. ;------ xy_to_regs --------------------------------------------
  209. ; on entry: x in cur_x, y in cur_y
  210. ; on exit:  dx = chars left on line, di = address
  211. ; Alters ax, bx.
  212. xy_to_regs    proc    near
  213.     ; Find number of chars 'till end of line, keep in DX
  214.     mov    ax, max_cur_x
  215.     mov    bx, ax            ; save max_x & cur_x for next block
  216.     mov    ah, 0            ; ax = max_x
  217.     xchg    dx, ax
  218.     mov    al, bh
  219.     mov    ah, 0            ; ax = cur_x
  220.     sub    dx, ax
  221.     inc    dx            ; dx is # of chars till EOL
  222.     ; Calculate DI = current address in text buffer
  223.     mov    al, bl            ; al = max_x
  224.     inc    al
  225.     mul    cur_y
  226.     add    al, bh            ; al += cur_x
  227.     adc    ah, 0            ; AX is # of chars into buffer
  228.     add    ax, ax
  229.     xchg    di, ax            ; DI is now offset of cursor.
  230.     ret
  231. xy_to_regs    endp
  232.  
  233.  
  234. ;------- dos_fn_tab -------------
  235. ; This table is used in "interrupt" to call the routine that handles
  236. ; the requested function.
  237.  
  238. max_cmd equ    12
  239. dos_fn_tab:
  240.     dw    dosfn0, nopcmd, nopcmd, badcmd, dosfn4, dosfn5, dosfn6
  241.     dw    dosfn7, dosfn8, dosfn8, nopcmd, nopcmd
  242.  
  243. ;------- strategy ----------------------------------------------------
  244. ; DOS calls strategy with a request which is to be executed later.
  245. ; Strategy just saves the request.
  246.  
  247. strategy    proc    far
  248.     mov    cs:req_off,BX
  249.     mov    cs:req_seg,ES
  250.     ret
  251. strategy    endp
  252.  
  253. ;------ interrupt -----------------------------------------------------
  254. ; This is where the request handed us during "strategy" is
  255. ; actually carried out.
  256. ; Calls one of 12 subroutines depending on the function requested.
  257. ; Each subroutine returns with exit status in AX.
  258.  
  259. interrupt    proc    far
  260.     sti
  261.     push_all            ; preserve caller's registers
  262.     push    ds
  263.     push    es
  264.  
  265.     ; Read requested function information into registers
  266.     lds    bx,cs:req_ptr
  267.     mov    al,[BX+02h]        ; al = function code
  268. ;
  269. ; The next instruction blows up MASM 1.0 but who cares!!
  270. ;
  271.     les    si,[BX+0Eh]        ; ES:SI = input/output buffer addr
  272.     mov    cx,[BX+12h]        ; cx = input/output byte count
  273.  
  274.     cmp    al, max_cmd
  275.     ja    unk_command        ; too big, exit with error code
  276.  
  277.     xchg    bx, ax
  278.     shl    bx, 1            ; form index to table of words
  279.     mov    ax, cs
  280.     mov    ds, ax
  281.     call    word ptr dos_fn_tab[bx]
  282. int_done:
  283.     lds    bx,cs:req_ptr        ; report status
  284.     or    ax, 100h        ; (always set done bit upon exit)
  285.     mov    [bx+03],ax
  286.  
  287.     pop    ES            ; restore caller's registers
  288.     pop    DS
  289.     pop_all
  290.     ret                ; return to DOS.
  291.  
  292. unk_command:
  293.     call    badcmd
  294.     jmp    int_done
  295.  
  296. interrupt    endp
  297.  
  298. ;----- BIOS break handler -----------------------------------------
  299. ; Called by BIOS when Control-Break is hit (vector was set up in Init).
  300. ; Simply notes that a break was hit.  Flag is checked during input calls.
  301.  
  302. break_handler    proc
  303.     mov    cs:brkkey.len, 1
  304.     iret
  305. break_handler    endp
  306.  
  307.     page
  308.  
  309. ;------ badcmd -------------------------------------------------------
  310. ; Invalid function request by DOS.
  311. badcmd    proc    near
  312.     mov    ax, 813h        ; return "Error: invalid cmd"
  313.     ret
  314. badcmd    endp
  315.  
  316.  
  317. ;------ nopcmd -------------------------------------------------------
  318. ; Unimplemented or dummy function request by DOS.
  319. nopcmd    proc    near
  320.     xor    ax, ax            ; No error, not busy.
  321.     ret
  322. nopcmd    endp
  323.  
  324. ;------- dos function #4 ----------------------------------------
  325. ; Reads CX characters from the keyboard, places them in buffer at
  326. ; ES:SI.
  327. dosfn4    proc    near
  328.     jcxz    dos4done
  329.     mov    di, si
  330. dos4lp: push    cx
  331.     call    getchar
  332.     pop    cx
  333.     stosb
  334.     loop    dos4lp
  335. dos4done:
  336.     xor    ax, ax            ; No error, not busy.
  337.     ret
  338. dosfn4    endp
  339.  
  340. ;-------- dos function #5: non-destructive input, no wait ------
  341. ; One-character lookahead into the keyboard buffer.
  342. ; If no characters in buffer, return BUSY; otherwise, get value of first
  343. ; character of buffer, stuff into request header, return DONE.
  344. dosfn5    proc    near
  345.     call    peekchar
  346.     jz    dos5_busy
  347.  
  348.     lds    bx,req_ptr
  349.     mov    [bx+0Dh], al
  350.     xor    ax, ax            ; No error, not busy.
  351.     jmp    short dos5_exit
  352. dos5_busy:
  353.     MOV    ax, 200h        ; No error, busy.
  354. dos5_exit:
  355.     ret
  356.  
  357. dosfn5    endp
  358.  
  359. ;-------- dos function #6: input status --------------------------
  360. ; Returns "busy" if no characters waiting to be read.
  361. dosfn6    proc    near
  362.     call    peekchar
  363.     mov    ax, 200h        ; No error, busy.
  364.     jz    dos6_exit
  365.     xor    ax, ax            ; No error, not busy.
  366. dos6_exit:
  367.     ret
  368. dosfn6    endp
  369.  
  370. ;-------- dos function #7: flush input buffer --------------------
  371. ; Clears the IBM keyboard input buffer.  Since it is a circular
  372. ; queue, we can do this without knowing the beginning and end
  373. ; of the buffer; all we need to do is set the tail of the queue
  374. ; equal to the head (as if we had read the entire queue contents).
  375. ; Also resets all the device driver's stuffahead buffers.
  376. dosfn7    proc    near
  377.     xor    ax, ax
  378.     mov    fnkey.len, ax        ; Reset the stuffahead buffers.
  379.     mov    cprseq.len, ax
  380.     mov    brkkey.len, ax
  381.     mov    xlatseq.len, ax
  382.  
  383.     mov    ax, abs40
  384.     mov    es, ax
  385.     mov    ax, es:buffer_head    ; clear queue by making the tail
  386.     mov    es:buffer_tail, ax    ; equal to the head
  387.  
  388.     xor    ax, ax            ; no error, not busy.
  389.     ret
  390. dosfn7    endp
  391.  
  392.     page
  393.     if    takeBIOS
  394. ;--- new_vid_bios -------------------------------------------
  395. ; New_vid_bios simply replaces the write_tty call.
  396. ; All other calls get sent to the old video bios.
  397. ; This gives BIOS ANSI capability.
  398. ; However, it takes away the escape character.
  399. ; If this is not desired, just tell init to not take over the vector.
  400.  
  401. new_vid_bios    proc
  402.     cmp    ah, 14
  403.     jz    nvb_write_tty
  404.     jmp    dword ptr cs:old_vid_bios
  405. nvb_write_tty:
  406.     push    cx
  407.     mov    cl, cs:cur_attrib
  408.     ; If in graphics mode, BL is new color
  409.     call    in_g_mode        ; returns carry set if text mode
  410.     jc    nvb_wt_text
  411.         mov    cs:cur_attrib, bl    ; ja?
  412. nvb_wt_text:
  413.     int    29h            ; write AL
  414.     mov    cs:cur_attrib, cl    ; restore color
  415.     pop    cx
  416.     iret
  417.  
  418. new_vid_bios    endp
  419.     endif
  420.  
  421. ;------ int_29 ----------------------------------------------
  422. ; Int 29 handles DOS quick-access putchar.
  423. ; Last device loaded with attribute bit 4 set gets accessed for
  424. ; single-character writes via int 29h instead of via interrupt.
  425. ; Must preserve all registers.
  426. ; Installed as int 29h by dosfn0 (init).
  427. int_29_buf    db    ?
  428.  
  429. int_29    proc    near
  430.     sti
  431.     push    ds
  432.     push    es
  433.     push_all
  434.     mov    cx, 1
  435.     mov    bx, cs
  436.     mov    es, bx
  437.     mov    ds, bx
  438.     mov    si, offset int_29_buf
  439.     mov    byte ptr [si], al
  440.     call    dosfn8
  441.     pop_all
  442.     pop    es
  443.     pop    ds
  444.     iret
  445. int_29    endp
  446.  
  447.     page
  448. ;------ dosfn8 -------------------------------------------------------
  449. ; Handles writes to the device (with or without verify).
  450. ; Called with
  451. ;  CX     = number of bytes to write
  452. ;  ES:SI = transfer buffer
  453. ;  DS     = CS, so we can access local variables.
  454.  
  455. dosfn8    proc    near
  456.  
  457.     mov    f_cptr_seg, es    ; save segment of char ptr
  458.  
  459.     ; Read the BIOS buffer address/cursor position variables.
  460.     mov    ax, abs40
  461.     mov    ds, ax
  462.     assume    ds:abs40
  463.  
  464.     ; Find current video mode and screen size.
  465.     mov    ax,word ptr crt_mode    ; al = crt mode; ah = # of columns
  466.     mov    cs:video_mode, al
  467.     dec    ah            ; ah = max column
  468.     mov    cs:max_x, ah
  469.  
  470.     ; Find current cursor coordinates.
  471.     mov    al,active_page
  472.     cbw
  473.     add    ax,ax
  474.     xchg    bx,ax
  475.     mov    ax,cursor_posn[bx]
  476.     mov    cs:cur_coords,AX
  477.  
  478.     ; Find video buffer segment address; adjust it
  479.     ; so the offset is zero; return in AX.
  480.  
  481.     ; DS is abs40.
  482.     ; Find 6845 address.
  483.     mov    ax, addr_6845
  484.     mov    cs:port_6845, ax
  485.     ; Find video buffer address.
  486.     MOV    AX,crt_start
  487.     shr    ax, 1
  488.     shr    ax, 1
  489.     shr    ax, 1
  490.     shr    ax, 1
  491.     add    ah, 0B0h        ; assume it's a monochrome card...
  492.     CMP    cs:video_mode,07
  493.     jz    d8_gots
  494.     add    ah, 8            ; but if not mode 7, it's color.
  495. d8_gots:
  496.     push    cs
  497.     pop    ds
  498.     assume    ds:code
  499.     mov    video_seg, ax
  500.     mov    es, ax
  501.     call    xy_to_regs        ; Set DX, DI according to cur_coords.
  502.  
  503.     ; | If in graphics mode, clear old pseudocursor
  504.     call    in_g_mode
  505.     jc    d8_no_cp
  506.         call    pseudocursor    ; write block in xor
  507. d8_no_cp:
  508.     mov    bx, xlate_tab_ptr    ; get pointer to translation table
  509.  
  510.     mov    ah, cur_attrib
  511.     mov    ds, f_cptr_seg        ; get segment of char ptr
  512.     assume    ds:nothing
  513.     cld                ; make sure we'll increment
  514.  
  515.     ; The Inner Loop: 4+12 +4+4 +4+4 +0+15 +4+4 +4+18 = 77 cycles/loop
  516.     ; on 8088; at 4.77 MHz, that gives 16.1 microseconds/loop.
  517.     ; At that speed, it takes 32 milliseconds to fill a screen.
  518.  
  519.     ; Get a character, put it on the screen, repeat 'til end of line
  520.     ; or no more characters.
  521.     jcxz    f_loopdone        ; if count = 0, we're already done.
  522.     cmp    cs:escvector, 0        ; If in middle of an escape sequence,
  523.     jnz    f_in_escapex        ; jump to escape sequence handler.
  524.  
  525. f_tloop:; | If in graphics mode, jump to alternate loop
  526.     ; | What a massive kludge!  A better approach would have been
  527.     ; | to collect characters for a "write n chars" routine
  528.     ; | which would handle both text and graphics modes.
  529.     call    in_g_mode
  530.     jc    f_t_cloop
  531.     jmp    f_g_cloop
  532.  
  533. f_t_cloop:
  534.     LODSB                ; get char! (al = ds:[si++])
  535.     cmp    al, 28            ; is it a control char?
  536.     jb    f_control        ;  maybe...
  537. f_t_nctl:
  538.     seg_cs
  539.     xlat
  540.     STOSW                ; Put Char! (es:[di++] = ax)
  541.     dec    dx            ; count down to end of line
  542.     loopnz    f_t_cloop        ; and go back for more.
  543.     jz    f_t_at_eol        ; at end of line; maybe do a crlf.
  544.     jmp    short f_loopdone
  545.  
  546. f_looploop:
  547. f_ansi_exit:                ; in case we switched into
  548.     loopnz    f_tloop            ; a graphics mode
  549. f_t_at_eol:
  550.     jz    f_at_eol
  551.  
  552. f_loopdone:
  553.  
  554.     ;--------- All done with write request -----------
  555.     ; DI is cursor address; cursor position in cur_y, dl.
  556.     mov    ax, cs
  557.     mov    ds, ax            ; get our segment back
  558.     assume    ds:code
  559.  
  560.     ; Restore cur_x = max_x - dx + 1.
  561.     mov    al, max_x
  562.     inc    al
  563.     sub    al, dl
  564.     mov    cur_x, al
  565.     ; Set cursor position; cursor adr in DI; cursor pos in cur_x,cur_y
  566.     call    set_pseudocursor
  567.     ; Return to DOS.
  568.     xor    ax, ax            ; No error, not busy.
  569.     ret
  570.  
  571.     ;---- handle control characters ----
  572.     ; Note: cur_x is not kept updated in memory, but can be
  573.     ; computed from max_x and dx.
  574.     ; Cur_y is kept updated in memory.
  575. f_control:
  576.     cmp    al, 27            ; Is it an escape?
  577.     jz    f_escapex
  578.     cmp    al, 13            ; carriage return?
  579.     jz    f_cr
  580.     cmp    al, 10            ; line feed?
  581.     jz    f_lf
  582.     cmp    al, 9            ; tab?
  583.     jz    f_tabx
  584.     cmp    al, 8            ; backspace?
  585.     jz    f_bs
  586.     cmp    al, 7            ; bell?
  587.     jz    f_bell
  588.     jmp    f_nctl            ; then it is not a control char.
  589.  
  590. f_tabx: jmp    f_tab
  591. f_escapex:
  592.     jmp    f_escape
  593. f_in_escapex:
  594.     jmp    f_in_escape
  595.  
  596. f_bs:    ;----- Handle backspace -----------------
  597.     ; Moves cursor back one space without erasing.    No wraparound.
  598.     cmp    dl, cs:max_x        ; wrap around to previous line?
  599.     ja    fbs_wrap        ; yep; disallow it.
  600.     dec    di            ; back up one char & attrib,
  601.     dec    di
  602.     inc    dx            ; and note one more char left on line.
  603. fbs_wrap:
  604.     jmp    f_looploop
  605.  
  606. f_bell: ;----- Handle bell ----------------------
  607.     ; Use BIOS to do the beep.  DX is not changed, as bell is nonprinting.
  608.     call    beep
  609.     or    al, al            ; clear z
  610. ; old (more portable) version:
  611. ;    mov    ax, 0e07h        ; "write bell to tty simulator"
  612. ;    mov    bx, 0            ; "page zero, color black"
  613. ;    int    10h            ; call BIOS
  614. ;    mov    ah, cs:cur_attrib    ; restore current attribute
  615. ;    mov    bx, cs:xlate_tab_ptr    ; restore translate table address
  616. ;    or    al, al            ; al still 7; this clears Z.
  617.     jmp    f_looploop        ; Let main loop decrement cx.
  618.  
  619. f_cr:    ;----- Handle carriage return -----------
  620.     ; di -= cur_x<<1;        set di= address of start of line
  621.     ; dx=max_x+1;            set bx= chars left in line
  622.     mov    al, cs:max_x
  623.     inc    al
  624.     sub    al, dl            ; Get cur_x into ax.
  625.     mov    ah, 0
  626.     sub    di, ax
  627.     sub    di, ax
  628.     mov    dl, cs:max_x        ; Full line ahead of us.
  629.     inc    dx
  630.     mov    ah, cs:cur_attrib    ; restore current attribute
  631.     or    al, 1            ; clear z
  632.     jmp    f_looploop        ; and let main loop decrement cx
  633.  
  634. f_at_eol:
  635.     ;----- Handle overrunning right end of screen -------
  636.     ; cx++;                compensate for double loop
  637.     ; if (!wrap_flag) { dx++; di-=2; }
  638.     ; else do_crlf;
  639.     inc    cx
  640.     test    cs:wrap_flag, 1
  641.     jnz    feol_wrap
  642.         dec    di
  643.         dec    di
  644.         inc    dx
  645.         jmp    f_looploop
  646. feol_wrap:
  647.     ; dx=max_x+1;            set bx= chars left in line
  648.     ; di -= 2*(max_x+1);
  649.     ; do_lf
  650.     mov    dl, cs:max_x
  651.     inc    dx
  652.     sub    di, dx
  653.     sub    di, dx
  654.     ; fall thru to line feed routine
  655.  
  656. f_lf:    ;----- Handle line feed -----------------
  657.     ; if (cur_y >= max_y) scroll;        scroll screen up if needed
  658.     ; else { cur_y++; di += max_x<<1;    else increment Y
  659.  
  660.     mov    al, cs:max_y
  661.     cmp    cs:cur_y, al
  662.     jb    flf_noscroll
  663.         call    scroll_up        ; preserves bx,cx,dx,si,di
  664.         jmp    short flf_done
  665. flf_noscroll:
  666.     inc    cs:cur_y
  667.     mov    al, cs:max_x
  668.     mov    ah, 0
  669.     inc    ax
  670.     add    ax, ax
  671.     add    di, ax
  672. flf_done:
  673.     mov    ah, cs:cur_attrib        ; restore current attribute
  674.     or    al, 1            ; clear z
  675.     jmp    f_looploop        ; and let main loop decrement cx
  676.  
  677. f_tab:    ;----- Handle tab expansion -------------
  678.     ; Get cur_x into al.
  679.     mov    al, cs:max_x
  680.     inc    al
  681.     sub    al, dl
  682.     ; Calculate number of spaces to output.
  683.     push    cx            ; save cx
  684.     mov    ch, 0
  685.     mov    cl, al            ; get zero based x coordinate
  686.     and    cl, 7
  687.     neg    cl
  688.     add    cl, 8            ; 0 -> 8, 1 -> 8, ... 7 -> 1
  689.     sub    dx, cx            ; update chars-to-eol, maybe set z
  690.     pushf                ; || save Z for main loop
  691.     ; ah is still current attribute.  Move CX spaces to the screen.
  692.     mov    al, ' '
  693.     call    in_g_mode        ; | graphics mode
  694.     jnc    f_tab_putc        ; |
  695.     REP    STOSW
  696.     popf                ; || restore Z flag for main loop test
  697.     pop    cx            ; restore cx
  698.     jmp    f_looploop        ; Let main loop decrement cx.
  699.  
  700. ;--------------- graphics mode support -----------------------
  701.  
  702. f_tab_putc:    ; graphics mode- call putc to put the char
  703.     add    dx, cx            ; move back to start of tab
  704. f_tp_lp:
  705.     call    putchar
  706.     dec    dx            ; go to next cursor position
  707.     loop    f_tp_lp
  708.     popf                ; Z set if wrapped around EOL
  709.     pop    cx
  710.     jmp    f_looploop
  711.  
  712. ;---- in_g_mode -------------
  713. ; Returns Carry set if not in a graphics mode.
  714. ; Preserves all registers.
  715.  
  716. in_g_mode    proc    near
  717.     cmp    cs:video_mode, 4
  718.     jb    igm_stc
  719.     cmp    cs:video_mode, 7
  720.     jz    igm_stc
  721.     clc
  722.     ret
  723. igm_stc:
  724.     stc
  725.     ret
  726. in_g_mode    endp
  727.  
  728. ;---- Where to go when a character turns out not to be special
  729. f_nctl:
  730. f_not_ansi:
  731.     call    in_g_mode
  732.     jnc    f_g_nctl        ; graphics mode
  733. f_jmptnctl:
  734.     jmp    f_t_nctl        ; text mode
  735.  
  736. ;---- Alternate main loop for graphics mode ----
  737. f_g_cloop:
  738.     LODSB                ; get char! (al = ds:[si++])
  739.     cmp    al, 28            ; is it a control char?
  740.     jb    f_g_control        ;  maybe...
  741. f_g_nctl:
  742.     seg_cs
  743.     xlat
  744.     call    putchar
  745.     dec    dx            ; count down to end of line
  746.     loopnz    f_g_cloop        ; and go back for more.
  747.     jz    f_g_at_eol        ; at end of line; maybe do a crlf.
  748.     jmp    f_loopdone
  749.  
  750. f_g_control:    jmp    f_control
  751. f_g_at_eol:    jmp    f_at_eol
  752.  
  753. ;---- putchar ------------------------------------------------
  754. ; Writes char AL, attribute AH to screen at (max_x+1-dl), cur_y.
  755. ; On entry, registers set up as per xy_to_regs.
  756. ; Preserves all registers.
  757. putchar proc    near
  758.     push    dx
  759.     push    cx
  760.     push    bx
  761.     push    ax
  762.     ; 1. Set cursor position.
  763.     mov    al, cs:max_x
  764.     inc    al
  765.     sub    al, dl
  766.     mov    cs:cur_x, al
  767.     mov    dx, cs:cur_coords    ; get X & Y into DX
  768.     xor    bx, bx            ; choose dpy page 0
  769.     mov    ah, 2            ; chose "Set Cursor Position"
  770.     int    10h            ; call ROM BIOS
  771.     ; 2. Write char & attribute.
  772.     mov    cx, 1
  773.     pop    ax            ; get char in AL
  774.     push    ax
  775.     mov    bl, ah            ; attribute in BL
  776.     mov    bh, 0
  777.     mov    ah, 9
  778.     int    10h
  779.     pop    ax
  780.     pop    bx
  781.     pop    cx
  782.     pop    dx
  783.     ret
  784. putchar endp
  785.  
  786. ;---- set_pseudocursor ------------
  787. ; If in graphics mode, set pseudocursor, else set real cursor.
  788. ; Destroys DS!!!!
  789.  
  790. set_pseudocursor    proc    near
  791.     call    in_g_mode
  792.     jnc    pseudocursor
  793. ; old (more portable, but slower) version
  794. ;    mov    dx, cur_coords        ; get X & Y into DX
  795. ;    xor    bx, bx            ; choose dpy page 0
  796. ;    mov    ah, 2            ; chose "Set Cursor Position"
  797. ;    int    10h            ; call ROM BIOS
  798.  
  799.     ; Write directly to 6845 cursor address register.
  800.     mov    bx, di
  801.     shr    bx, 1            ; convert word index to byte index
  802.  
  803.     mov    dx, port_6845
  804.     mov    al, 0eh
  805.     out    dx, al
  806.  
  807.     jmp    $+2
  808.     inc    dx
  809.     mov    al, bh
  810.     out    dx, al
  811.  
  812.     jmp    $+2
  813.     dec    dx
  814.     mov    al, 0fh
  815.     out    dx, al
  816.  
  817.     jmp    $+2
  818.     inc    dx
  819.     mov    al, bl
  820.     out    dx, al
  821.  
  822.     ; Set cursor position in low memory.
  823.     assume    ds:abs40
  824.     mov    ax, abs40
  825.     mov    ds, ax
  826. ; Does anybody ever use anything but page zero?
  827. ;    mov    al,active_page
  828. ;    cbw
  829. ;    add    ax,ax
  830. ;    xchg    bx,ax
  831.     mov    ax, cs:cur_coords
  832.     mov    cursor_posn,ax
  833.     ret
  834.  
  835.     assume    ds:code
  836. set_pseudocursor    endp
  837.  
  838.  
  839. ;---- pseudocursor --------------------------------------------------
  840. ; Writes a color 15 block in XOR at the current cursor location.
  841. ; Preserves DS, ES, BX, CX, DX, SI, DI.
  842. ; Should be disableable- the pseudocursor slows down single-char
  843. ; writes by a factor of three.
  844. pseudocursor    proc    near
  845.     mov    ax, 8f16h    ; xor, color 15, ^V (small block)
  846.     call    putchar
  847.     ret
  848. pseudocursor    endp
  849.  
  850. ;--------------- end of graphics mode support --------------------
  851.  
  852. dosfn8    endp
  853.  
  854. ;--- get_blank_attrib ------------------------------------------------
  855. ; Determine new attribute and character for a new blank region.
  856. ; Use current attribute, just disallow blink and underline.
  857. ; (Pretty strange way to do it.  Might want to disallow rev vid, too.)
  858. ; Returns result in AH, preserves all other registers.
  859. get_blank_attrib    proc    near
  860.     mov    ah, 0
  861.     call    in_g_mode
  862.     jnc    gb_aok            ; if graphics mode, 0 is bkgnd
  863.  
  864.     mov    ah, cs:cur_attrib
  865.     and    ah, 7fh            ; disallow blink
  866.     cmp    cs:video_mode, 7    ; monochrome?
  867.     jnz    gb_aok
  868.         cmp    ah, 1        ; underline?
  869.         jnz    gb_aok
  870.         mov    ah, 7        ; yep- set it to normal.
  871. gb_aok: ret
  872. get_blank_attrib    endp
  873.  
  874.  
  875. ;---- scroll_up ---------------------------------------------------
  876. ; Scroll screen up- preserves ax, bx, cx, dx, si, di, ds, es.
  877. ; Moves screen up 1 line, fills the last line with blanks.
  878. ; Attribute of blanks is the current attribute sans blink and underline.
  879.  
  880. scroll_up    proc    near
  881.     push    ax
  882.     push    bx
  883.     push    cx
  884.     push    dx
  885.  
  886.     call    get_blank_attrib
  887.     mov    bh, ah            ; color to use on new blank areas
  888.     mov    al, 1            ; AL is number of lines to scroll.
  889.     mov    ah, 6            ; BIOS: scroll up
  890.     mov    cl, 0            ; upper-left-x of data to scroll
  891.     mov    ch, 0            ; upper-left-y of data to scroll
  892.     mov    dl, cs:max_x        ; lower-rite-x
  893.     mov    dh, cs:max_y        ; lower-rite-y (zero based)
  894.     int    10h            ; call BIOS to scroll a rectangle.
  895.  
  896.     pop    dx
  897.     pop    cx
  898.     pop    bx
  899.     pop    ax
  900.     ret
  901. scroll_up    endp
  902.  
  903. ;---- lookup -----------------------------------------------
  904. ; Called by getchar, peekchar, and key to see if a given key has
  905. ; been redefined.
  906. ; Sets AH to zero if AL is not zero (i.e. if AX is not a function key).
  907. ; Returns with Z cleared if no redefinition; otherwise,
  908. ; Z is set, SI points to redefinition string, CX is its length.
  909. ; Preseves AL, all but CX and SI.
  910. ; Redefinition table organization:
  911. ;  Strings are stored in reversed order, first char last.
  912. ;  The word following the string is the character to be replaced;
  913. ;  the next word is the length of the string sans header.
  914. ; param_end points to the last byte used by the parameter buffer;
  915. ; redef_end points to the last word used by the redef table.
  916.  
  917. lookup    proc    near
  918.     mov    si, redef_end        ; Start at end of table, move down.
  919.     or    al, al
  920.     jz    lu_lp
  921.     mov    ah, 0            ; clear extraneous scan code
  922. lu_lp:    cmp    si, param_end
  923.     jbe    lu_notfound        ; If below redef table, exit.
  924.     mov    cx, [si]
  925.     cmp    ax, [si-2]        ; are you my mommy?
  926.     jz    lu_gotit
  927.     sub    si, 4
  928.     sub    si, cx            ; point to next header
  929.     jmp    lu_lp
  930. lu_notfound:
  931.     or    si, si            ; clear Z
  932.     jmp    short lu_exit
  933. lu_gotit:
  934.     sub    si, 2
  935.     sub    si, cx            ; point to lowest char in memory
  936.     cmp    al, al            ; set Z
  937. lu_exit:
  938.     ret
  939. lookup    endp
  940.  
  941. ;---- searchbuf --------------------------------------------
  942. ; Called by getchar and peekchar to see if any characters are
  943. ; waiting to be gotten from sources other than BIOS.
  944. ; Returns with Z set if no chars found, BX=keybuf & SI=keybuf.len otherwise.
  945. searchbuf    proc    near
  946.     ; Search the stuffahead buffers.
  947.     mov    cx, 4            ; number of buffers to check for chars
  948.     mov    bx, offset fnkey - 4
  949. sbloop: add    bx, 4            ; point to next buffer record
  950.     mov    si, [bx].len
  951.     or    si, si            ; empty?
  952.     loopz    sbloop            ; if so, loop.
  953.     ret
  954. searchbuf    endp
  955.  
  956. ;---- getchar -----------------------------------------------
  957. ; Returns AL = next char.
  958. ; Trashes AX, BX, CX, BP, SI.
  959. getchar proc    near
  960. gc_searchbuf:
  961.     ; See if any chars are waiting in stuffahead buffers.
  962.     call    searchbuf
  963.     jz    gc_trykbd        ; No chars?  Try the keyboard.
  964.     ; A nonempty buffer was found.
  965.     dec    [bx].len
  966.     dec    si
  967.     mov    bp, [bx].adr        ; get pointer to string
  968.     mov    al, byte ptr ds:[bp][si]; get the char
  969.     ; Recognize function key sequences, move them to highest priority
  970.     ; queue.
  971.     sub    si, 1            ; set carry if si=0
  972.     jc    gc_nofnkey        ; no chars left -> nothing to protect.
  973.     cmp    bx, offset fnkey
  974.     jz    gc_nofnkey        ; already highest priority -> done.
  975.     or    al, al
  976.     jnz    gc_nofnkey        ; nonzero first byte -> not fnkey.
  977.     ; Found a function key; move it to highest priority queue.
  978.     dec    [bx].len
  979.     mov    ah, byte ptr ds:[bp][si]; get the second byte of fn key code
  980. gc_fnkey:
  981.     mov    fnkey.len, 1
  982.     mov    fnkeybuf, ah        ; save it.
  983. gc_nofnkey:
  984.     ; Valid char in AL.  Return with it.
  985.     jmp    short gcdone
  986.  
  987. gc_trykbd:
  988.     ; Actually get a character from the keyboard.
  989.     mov    ah, 0
  990.     int    16h            ; BIOS returns with char in AX
  991.     ; If it's Ctrl-break, it has already been taken care of.
  992.     or    ax, ax
  993.     jz    gc_trykbd
  994.  
  995.     ; Look in the reassignment table to see if it needs translation.
  996.     call    lookup            ; Z=found; CX=length; SI=ptr
  997.     jnz    gc_noredef
  998.     ; Okay; set up the reassignment, and run thru the translation code.
  999.     mov    xlatseq.len, cx
  1000.     mov    xlatseq.adr, si
  1001.     jmp    gc_searchbuf
  1002. gc_noredef:
  1003.     ; Is it a function key?
  1004.     cmp    al, 0
  1005.     jz    gc_fnkey        ; yep- special treatment.
  1006. gcdone: ret    ; with character in AL.
  1007.  
  1008. getchar endp
  1009.  
  1010. ;---- peekchar -----------------------------------------------
  1011. ; Returns Z if no character ready, AL=char otherwise.
  1012. ; Trashes AX, BX, CX, BP, SI.
  1013. peekchar    proc    near
  1014. pc_searchbuf:
  1015.     call    searchbuf
  1016.     jz    pc_trykbd        ; No chars?  Try the keyboard.
  1017.     ; A nonempty buffer was found.
  1018.     dec    si
  1019.     mov    bp, [bx].adr        ; get pointer to string
  1020.     mov    al, byte ptr ds:[bp][si]; get the char
  1021.     ; Valid char from buffer in AL.  Return with it.
  1022.     jmp    short pcdone
  1023. pc_trykbd:
  1024.     ; Actually peek at the keyboard.
  1025.     mov    ah, 1
  1026.     int    16h            ; BIOS returns with char in AX
  1027.     jz    pcexit
  1028.     ; If it's control-break, it's already been taken care of.
  1029.     or    ax, ax
  1030.     jnz    pc_notbrk
  1031.     mov    ah, 0
  1032.     int    16h            ; so get rid of it!
  1033.     jmp    short pc_trykbd
  1034. pc_notbrk:
  1035.     ; Look in the reassignment table to see if it needs translation.
  1036.     call    lookup            ; Z=found; CX=length; SI=ptr
  1037.     jnz    pcdone            ; Nope; just return the char.
  1038.     ; Okay; get the first code to be returned.
  1039.     add    si, cx
  1040.     mov    al, [si-1]
  1041. pcdone: or    ah, 1            ; NZ; char ready!
  1042. pcexit: ret    ; with character in AL, Z true if no char waiting.
  1043. peekchar    endp
  1044.  
  1045. ;---- beep ------------------------------------------------------
  1046. ; Beep speaker; period given by beep_div, duration by beep_len.
  1047. ; Preserves all registers.
  1048.  
  1049. beep_div    dw    1300        ; fairly close to IBM beep
  1050. beep_len    dw    3        ; 3/18 sec- shorter than IBM
  1051.  
  1052. beep    proc    near
  1053.     push_all
  1054.  
  1055.     mov    al, 10110110b        ; select 8253
  1056.     mov    dx, 43h            ; control port address
  1057.     out    dx, al
  1058.     dec    dx            ; timer 2 address
  1059.     mov    ax, cs:beep_div
  1060.     jmp    $+2
  1061.     out    dx, al            ; low byte of divisor
  1062.     xchg    ah, al
  1063.     jmp    $+2
  1064.     out    dx, al            ; high byte of divisor
  1065.     mov    dx, 61h
  1066.     jmp    $+2
  1067.     in    al, dx            ; get current value of control bits
  1068.     push    ax
  1069.     or    al, 3
  1070.     jmp    $+2
  1071.     out    dx, al            ; turn speaker on
  1072.  
  1073.     ; Wait for desired duration by monitoring time-of-day 18 Hz clock
  1074.     push    es
  1075.     mov    ax, abs40
  1076.     mov    es, ax
  1077.     assume    es:abs40
  1078.     mov    bx, timer_low
  1079.     mov    cx, -1
  1080. beeplp:    mov    ax, timer_low
  1081.     sub    ax, bx
  1082.     cmp    ax, cs:beep_len
  1083.     jg    beepover
  1084.     loop    beeplp
  1085. beepover:
  1086.     pop    es
  1087.     assume    es:code
  1088.  
  1089.     ; Turn off speaker
  1090.     pop    ax
  1091.     and    al, not 3        ; turn speaker off
  1092.     out    dx, al
  1093.     pop_all
  1094.     ret
  1095. beep    endp
  1096.  
  1097. CODE    ends
  1098.  
  1099.     end                ; of nansi.asm
  1100.